# -*- coding: utf-8 -*-

import base64
from pathlib import Path
import re

from text_edit import TextEdit
from line_edit import LineEdit
import random
from PyQt5.QtWidgets import QFileDialog, QLineEdit, QMainWindow, QMessageBox

from loading_mask import LoadingMask
from gen_rsa_key_thread import ThreadGenRsaKey
from ui.Ui_main import Ui_MainWindow

from crypto import *

class MainWindow(QMainWindow, Ui_MainWindow):
    MODE_ECB = "ECB"
    MODE_CBC = "CBC"

    ENCODING_GBK = "gbk"
    ENCODING_UTF8 = "utf-8"

    SM2_ENTL = "0080"
    SM2_ID = "31323334353637383132333435363738"
    SM2_A = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC"
    SM2_B = "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93"
    SM2_XG = "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7"
    SM2_YG = "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
    

    def __init__(self, rootPath):
        super().__init__()
        self.rootPath = rootPath
        self.initData()
        self.setupUi(self)
        self.initUi()
        self.initEditTextCountSlot()
        

    def initData(self):
        self.symmetricAlg = DES3
        self.mode = self.MODE_ECB
        self.encoding = self.ENCODING_GBK
        self.modulusSize = 2048
        self.E = RSA.DefaultE
        self.rsaSignHash = SHA256
        self.isRsaEncryptAutoPad = True
        self.isRsaSignAutoPad = True
        self.genKeyThread = None
        self.isFixSm2EncryptRandom = True
        self.isFixSm2SignRandom = True
        self.isSymmetricAutoPad = False

    def initUi(self):
        self.loading = None
        self.loadingGif = {'gif': str(Path(self.rootPath) /'res'/'loading_small'), 'size': (200, 200)}
        self.lineEditENTL.setCursorPosition(0)
        self.lineEditID.setCursorPosition(0)
        self.lineEditA.setCursorPosition(0)
        self.lineEditB.setCursorPosition(0)
        self.lineEditxG.setCursorPosition(0)
        self.lineEdityG.setCursorPosition(0)
        self.tabWidget.removeTab(self.tabWidget.indexOf(self.tabChecksum))

    def initEditTextCountSlot(self):
        for obj in self.__dict__.values():
            if isinstance(obj, (LineEdit, TextEdit)):
                obj.showTextCount.connect(self.showTextCount)
                obj.textChanged.connect(self.showTextCount)
                if obj.isReadOnly():
                    obj.setStyleSheet(("TextEdit, LineEdit {border-color: lightgrey; color:grey;}"))

    def showTextCount(self, text=None):
        if text is None:
            if isinstance(self.sender(), LineEdit):
                text = self.sender().text()
            else:
                text = self.sender().toPlainText()
        if text is None:
            text = ""
        binSize = self.binSize(text)
        self.statusBar().showMessage(f'{self.sender().objectName()} 字符串长 {len(text)}， 二进制 {binSize} B')

    def setSymmetricAlg(self, checked):
        if not checked:
            return
        sender = self.sender()
        if sender == self.radioButtonAlgDes:
            self.symmetricAlg = DES
        elif sender == self.radioButtonAlg3Des2Key:
            self.symmetricAlg = DES3
        elif sender == self.radioButtonAlg3Des3Key:
            self.symmetricAlg = DES33
        elif sender == self.radioButtonAlgAes:
            self.symmetricAlg = AES
        elif sender == self.radioButtonAlgSm4:
            self.symmetricAlg = SM4
        self.setKeyIvPlaceHold()    

    def setKeyIvPlaceHold(self):
        if self.mode == self.MODE_ECB:
            self.lineEditIv.setReadOnly(True)
            self.lineEditIv.setPlaceholderText("Ignore")
        else:
            self.lineEditIv.setReadOnly(False)
            self.lineEditIv.setPlaceholderText(f"{self.blockSize()} B Hex String")
        self.lineEditKey.setPlaceholderText(f"{self.keySize()} B Hex String")    

    def setSymmetricMode(self, checked):
        if not checked:
            return
        sender = self.sender()
        if sender == self.radioButtonEcb:
            self.mode = self.MODE_ECB            
        elif sender == self.radioButtonCbc:
            self.mode = self.MODE_CBC
        self.setKeyIvPlaceHold()
    
    def keySize(self):
        keySize = AES.KeySize
        if self.symmetricAlg == DES:
            keySize = DES.KeySize
        elif self.symmetricAlg == DES33:
            keySize = DES33.KeySize
        return keySize

    def blockSize(self):
        blockSize = AES.BlockSize
        if self.symmetricAlg in [DES, DES3, DES33]:
            blockSize = DES.BlockSize
        return blockSize        

    def genSymmetricKey(self):
        key = randomBytes(self.keySize())
        self.setBin(self.lineEditKey, key)
        

    def genSymmetricIv(self):
        iv = randomBytes(self.blockSize())
        self.setBin(self.lineEditIv, iv)

    def genSymmetricInput(self):
        input = randomBytes(random.randint(1, 10) * self.blockSize())
        self.setBin(self.textEditSymmetricInput, input)

    def copyOutput(self):
        pass

    def size(self, who):
        return self.binSize(self.text(who))

    def binSize(self, text):
        try:
            b = bytes.fromhex(text)
            return len(b)
        except:
            return 0        

    def setBin(self, who, bin):
        if isinstance(who, LineEdit):
            who.setText(bin.hex().upper())
        else:
            who.setPlainText(bin.hex().upper())

    def setText(self, who, text, upper=True):
        if upper:
            text = text.upper()
        if isinstance(who, (QLineEdit, LineEdit)):
            who.setText(text)
        else:
            who.setPlainText(text)     

    def bin(self, who):
        try:
            b = bytes.fromhex(self.text(who))
            return b
        except:
            return bytes()               

    def text(self, who):
        text = None
        if isinstance(who, LineEdit):
            text = who.text()
        else:
            text = who.toPlainText()
        if text is None:
            text = ""       
        return text

    def bigIntText(self, who):
        text = self.text(who)
        text = re.sub(r'\s+', ' ', text)        
        if len(text) % 2 != 0:
            text = '0' + text
        return text

    def bigIntBin(self, who):
        try:
            b = bytes.fromhex(self.bigIntText(who))
            return b
        except:
            return bytes()            

    def checkSymmetric(self):
        error = ''

        # 密钥长度是否合适
        if self.size(self.lineEditKey) != self.keySize():
            error += f'密钥长度应为{self.keySize()}\n'
        
        # 数据长度是否合适
        if self.size(self.textEditSymmetricInput) == 0 or (not self.isSymmetricAutoPad and self.size(self.textEditSymmetricInput) % self.blockSize() != 0):
            error += f'数据长度应为{self.blockSize()}的整数倍\n'        

        # IV是否存在
        if self.mode == self.MODE_CBC:
            if self.size(self.lineEditIv) != self.blockSize():
                error += f'IV长度应为{self.blockSize()}\n'

        if (len(error) == 0):
            return True, None
        return False, error

    def showError(self, error):
        QMessageBox.critical(self, '错误', error)

    def symmetric(self, encrypt = True):
        ok, error = self.checkSymmetric()
        if not ok:
            self.showError(error)
            return
        key = self.bin(self.lineEditKey)
        iv = self.bin(self.lineEditIv)
        input = self.bin(self.textEditSymmetricInput)       
        
        if self.mode == self.MODE_ECB:
            iv = None
        cipher = self.symmetricAlg(key, iv=iv)                                  
        msg = None
        if encrypt:
            msg = cipher.encrypt(input, pad=self.isSymmetricAutoPad)
        else:
            msg = cipher.decrypt(input, pad=self.isSymmetricAutoPad)
        self.setBin(self.textEditSymmetricOutput, msg)

    def symmetricEncrypt(self):
        self.symmetric(True)

    def symmetricDecrypt(self):
        self.symmetric(False)

    def exchangeSymmetricData(self):
        input = self.text(self.textEditSymmetricInput)
        output = self.text(self.textEditSymmetricOutput)
        self.setText(self.textEditSymmetricInput, output)
        self.setText(self.textEditSymmetricOutput, input)

    def setModulusSize(self, checked):
        if not checked:
            return
        if self.sender() == self.radioButtonRSAModulusSize1024:
            self.modulusSize = 1024
        elif self.sender() == self.radioButtonRSAModulusSize2048:
            self.modulusSize = 2048

    def genaratedKey(self, key):
        self.loading.close()
        (n, e), (d, p, q, dp, dq, qinv) = key

        self.setBin(self.textEditRSAN, n)
        self.setBin(self.textEditRSAD, d)
        self.setBin(self.textEditRSAP, p)
        self.setBin(self.textEditRSAQ, q)
        self.setBin(self.textEditRSADP, dp)
        self.setBin(self.textEditRSADQ, dq)
        self.setBin(self.textEditRSAQINV, qinv)

    def genarateError(self, e):
        self.loading.close()
        self.showError(str(e))

    def genRsaKey(self):
        try:
            e = int(self.text(self.lineEditRSAE))
        except:
            self.showError('请输入正确的E值，应为大于1的奇数\n')
            return
        self.loading = LoadingMask(self, gif=self.loadingGif['gif'], gifSize=self.loadingGif['size'])
        self.loading.show()
        self.genKeyThread = ThreadGenRsaKey(self.modulusSize, e=e)
        self.genKeyThread.genaratedKey.connect(self.genaratedKey)
        self.genKeyThread.genarateError.connect(self.genarateError)        
        self.genKeyThread.start()

    def completeRsaKeyByPQE(self, p, q, e):
        ((n, e), (d, p, q, dp, dq, qinv)) = RSA.calcKey(p, q, e)
        
        return ((n, e), (d, p, q, dp, dq, qinv))    

    def checkRsaKey(self):
        pass

    def completeRsaKey(self):
        error = ''
        if self.size(self.textEditRSAP) == 0:
            error += '请输入正确的16进制P值\n'
        if self.size(self.textEditRSAQ) == 0:
            error += '请输入正确的16进制Q值\n'
        try:
            e = int(self.text(self.lineEditRSAE))
        except:
            error += '请输入正确的E值，应为大于1的奇数\n'
            return            
        if len(error) != 0:
            self.showError(error)
            return

        p = self.bigIntBin(self.textEditRSAP)
        q = self.bigIntBin(self.textEditRSAQ)

        ((n, e), (d, p, q, dp, dq, qinv)) = self.completeRsaKeyByPQE(p, q, e)

        self.setBin(self.textEditRSAN, n.to_bytes(self.modulusSize // 8, 'big'))
        self.setBin(self.textEditRSAD, d.to_bytes(self.modulusSize // 8, 'big'))
        self.setBin(self.textEditRSADP, dp.to_bytes(self.modulusSize // 8 // 2, 'big'))
        self.setBin(self.textEditRSADQ, dq.to_bytes(self.modulusSize // 8 // 2, 'big'))
        self.setBin(self.textEditRSAQINV, qinv.to_bytes(self.modulusSize // 8 // 2, 'big'))

    def saveRsaKey(self):
        pass

    def copyRsaKey(self):
        if self.sender() == self.pushButtonCopyRSAKeyEncrypt:
            self.setText(self.lineEditRSAEEncrypt, self.text(self.lineEditRSAE))
            self.setText(self.textEditRSANEncrypt, self.text(self.textEditRSAN))
            self.setText(self.textEditRSADEncrypt, self.text(self.textEditRSAD))
        elif self.sender() == self.pushButtonCopyRSAKeySign:
            self.setText(self.lineEditRSAESign, self.text(self.lineEditRSAE))
            self.setText(self.textEditRSANSign, self.text(self.textEditRSAN))
            self.setText(self.textEditRSADSign, self.text(self.textEditRSAD))            

    def randomRsaEncryptInput(self):
        pass

    def checkDoSK(self, autoPadEnabled=True):
        error = ''
        if self.size(self.textEditRSANEncrypt) == 0:
            error += '请输入正确的16进制N值\n'            
        if self.size(self.textEditRSADEncrypt) == 0:
            error += '请输入正确的16进制D值\n'         
        try:
            e = int(self.text(self.lineEditRSAEEncrypt))
        except:
            error += '请输入正确的E值，应为大于1的奇数\n'
        if self.size(self.textEditRSAInputEncrypt) == 0:
            error += '请输入正确的16进制值\n'
        n = int.from_bytes(self.bigIntBin(self.textEditRSANEncrypt), byteorder='big')
        d = int.from_bytes(self.bigIntBin(self.textEditRSADEncrypt), byteorder='big')
        input = int.from_bytes(self.bigIntBin(self.textEditRSAInputEncrypt), byteorder='big')
        if input >= n or input == 0:
            error += '数据应小于N且不为0\n'
        return error

    def checkDoPK(self):
        error = ''
        if self.size(self.textEditRSANEncrypt) == 0:
            error += '请输入正确的16进制N值\n'         
        try:
            e = int(self.text(self.lineEditRSAEEncrypt))
        except:
            error += '请输入正确的E值，应为大于1的奇数\n'
        if self.size(self.textEditRSAInputEncrypt) == 0:
            error += '请输入正确的16进制值\n'
        n = int.from_bytes(self.bigIntBin(self.textEditRSANEncrypt), byteorder='big')
        input = int.from_bytes(self.bigIntBin(self.textEditRSAInputEncrypt), byteorder='big')
        if input >= n or input == 0:
            error += '数据应小于N且不为0\n'
        return error        

    def rsaEncryptWithPK(self):
        error = self.checkDoPK()
        if len(error) != 0:
            self.showError(error)
            return         

        n = self.bigIntBin(self.textEditRSANEncrypt)
        e = int(self.text(self.lineEditRSAEEncrypt))
        input = self.bigIntBin(self.textEditRSAInputEncrypt)
        rsa = RSA(n, e)
        output = rsa.encrypt(input, pad=self.isRsaEncryptAutoPad)
        self.setBin(self.textEditRSAOutputEncrypt, output)        

    def rsaDecryptWithSK(self):
        error = self.checkDoSK()
        if len(error) != 0:
            self.showError(error)
            return

        n = self.bigIntBin(self.textEditRSANEncrypt)
        e = int(self.text(self.lineEditRSAEEncrypt))
        d = self.bigIntBin(self.textEditRSADEncrypt)
        input = self.bigIntBin(self.textEditRSAInputEncrypt)
        rsa = RSA(n, e, d=d)
        output = rsa.decrypt(input, pad=self.isRsaEncryptAutoPad)
        self.setBin(self.textEditRSAOutputEncrypt, output)        

    def rsaEncryptWithSK(self):
        error = self.checkDoSK()
        if len(error) != 0:
            self.showError(error)
            return   

        n = self.bigIntBin(self.textEditRSANEncrypt)
        e = int(self.text(self.lineEditRSAEEncrypt))
        d = self.bigIntBin(self.textEditRSADEncrypt)
        input = self.bigIntBin(self.textEditRSAInputEncrypt)
        rsa = RSA(n, e, d=d)
        output = rsa.encrypt(input, usePrivateKey=True)
        self.setBin(self.textEditRSAOutputEncrypt, output)              

    def rsaDecryptWithPK(self):
        error = self.checkDoPK()
        if len(error) != 0:
            self.showError(error)
            return
        n = self.bigIntBin(self.textEditRSANEncrypt)
        e = int(self.text(self.lineEditRSAEEncrypt))
        input = self.bigIntBin(self.textEditRSAInputEncrypt)
        rsa = RSA(n, e)
        output = rsa.decrypt(input, usePublicKey=True)
        self.setBin(self.textEditRSAOutputEncrypt, output)               

    def copyOutput(self):
        pass

    def setRsaEncryptAutoPad(self, checked):
        self.isRsaEncryptAutoPad = checked

    def exchangeRsaEncryptData(self):
        input = self.text(self.textEditRSAInputEncrypt)
        output = self.text(self.textEditRSAOutputEncrypt)
        self.setText(self.textEditRSAInputEncrypt, output)
        self.setText(self.textEditRSAOutputEncrypt, input)

    def randomRsaSignMessage(self):
        pass

    def setRsaSignAutoPad(self, checked):
        self.isRsaSignAutoPad = checked        

    def rsaVerify(self):
        error = ''
        if self.size(self.textEditRSANSign) == 0:
            error += '请输入正确的16进制N值\n'   
        if self.size(self.textEditRSAInputSign) == 0 and self.size(self.lineEditRSASignHash) == 0:
            error += '请输入正确的16进制消息值 或 HASH值\n'   
        if self.size(self.textEditRSAOutputSign) == 0:
            error += '请输入正确的16进制签名值\n'               

        try:
            e = int(self.text(self.lineEditRSAESign))
        except:
            error += '请输入正确的E值，应为大于1的奇数\n'
        if len(error) != 0:
            self.showError(error)
            return            

        digest = self.bigIntBin(self.lineEditRSASignHash)
        n = self.bigIntBin(self.textEditRSANSign)
        d = self.bigIntBin(self.textEditRSADSign)
        sign = self.bigIntBin(self.textEditRSAOutputSign)
        input = self.bigIntBin(self.textEditRSAInputSign)

        result = False
        rsa = RSA(n, e, d=d)
        if len(input) != 0:
            result = rsa.verify(sign, input, pad=self.isRsaSignAutoPad, digestHook=lambda digest: self.setBin(self.lineEditRSASignHash, digest))
        else:
            result = rsa.verifyDigest(sign, digest, pad=self.isRsaSignAutoPad)
        if result:
            QMessageBox.information(self, '正确', '验签通过')
        else:
            QMessageBox.warning(self, '错误', '验签不通过')

    def rsaSign(self):
        error = ''
        if self.size(self.textEditRSANSign) == 0:
            error += '请输入正确的16进制N值\n'            
        if self.size(self.textEditRSADSign) == 0:
            error += '请输入正确的16进制D值\n'       
        if self.size(self.textEditRSAInputSign) == 0 and self.size(self.lineEditRSASignHash) == 0:
            error += '请输入正确的16进制消息值 或 HASH值\n'                 
        try:
            e = int(self.text(self.lineEditRSAESign))
        except:
            error += '请输入正确的E值，应为大于1的奇数\n'
        if len(error) != 0:
            self.showError(error)
            return

        digest = self.bigIntBin(self.lineEditRSASignHash)
        n = self.bigIntBin(self.textEditRSANSign)
        d = self.bigIntBin(self.textEditRSADSign)        
        input = self.bigIntBin(self.textEditRSAInputSign)

        output = None
        rsa = RSA(n, e, d=d)        
        if len(input) != 0:
            output = rsa.sign(input, self.rsaSignHash, pad=self.isRsaSignAutoPad, digestHook=lambda digest: self.setBin(self.lineEditRSASignHash, digest))
        else:
            output = rsa.signDigest(digest, pad=self.isRsaSignAutoPad)
        self.setBin(self.textEditRSAOutputSign, output)        

    def genSm2Key(self):
        pubKey, priKey = SM2.genKey()
        self.setBin(self.lineEditSM2PK, pubKey)
        self.setBin(self.lineEditSM2SK, priKey)

    def checkSm2Key(self):
        pass

    def saveSm2Key(self):
        pass

    def randomSm2EncryptInput(self):
        pass

    def sm2Encrypt(self):
        error = ''
        if self.size(self.lineEditSM2PK) != SM2.PublicKeySize:
            error += f'请输入正确的16进制公钥值，公钥长应为 {SM2.PublicKeySize}\n'
        if self.size(self.textEditSM2InputEncrypt) == 0:
            error += '请输入正确的16进制值\n'        
        if self.isFixSm2EncryptRandom and self.size(self.lineEditSM2RandomEncrypt) != SM2.RandomSize:
            error += f'请输入正确的随机数，随机数长应为 {SM2.RandomSize}'
        if len(error) != 0:
            self.showError(error)
            return

        pk = self.bin(self.lineEditSM2PK)
        input = self.bin(self.textEditSM2InputEncrypt)
        random = None
        if self.isFixSm2EncryptRandom:            
            random = self.bin(self.lineEditSM2RandomEncrypt)
        sm2 = SM2(pk)
        output = sm2.encrypt(input, random=random, randomHook=lambda random: self.setBin(self.lineEditSM2RandomEncrypt, random))
        self.setBin(self.textEditSM2OutputEncrypt, output)
        
    def sm2Decrypt(self):
        error = ''
        if self.size(self.lineEditSM2SK) != SM2.PrivateKeySize:
            error += '请输入正确的16进制私钥值\n'
        if self.size(self.textEditSM2InputEncrypt) == 0:
            error += '请输入正确的16进制值\n'
        if len(error) != 0:
            self.showError(error)
            return

        sk = self.bin(self.lineEditSM2SK)
        input = self.bin(self.textEditSM2InputEncrypt)
        sm2 = SM2(sk)     
        output = sm2.decrypt(input)
        self.setBin(self.textEditSM2OutputEncrypt, output)

    def exchangeSm2EncryptData(self):
        input = self.text(self.textEditSM2InputEncrypt)
        output = self.text(self.textEditSM2OutputEncrypt)
        self.setText(self.textEditSM2InputEncrypt, output)
        self.setText(self.textEditSM2OutputEncrypt, input)

    def copySm2Key(self):
        pk = self.bin(self.lineEditSM2PK)
        sk = self.bin(self.lineEditSM2SK)
        self.setBin(self.lineEditSM2PKSign, pk)
        self.setBin(self.lineEditSM2SKSign, sk)

    def sm2Verify(self):
        error = ''
        if self.size(self.lineEditSM2PKSign) != SM2.PublicKeySize:
            error += f'请输入正确的16进制公钥值，公钥长应为 {SM2.PublicKeySize}\n'
        if self.size(self.textEditSM2SignOutput) != SM2.SignSize:
            error += f'请输入正确的16进制签名值，签名值长应为 {SM2.SignSize}\n'            
        if self.size(self.textEditSM2SignMessage) == 0 and self.size(self.lineEditSM2SignHash) == 0:
            error += '请输入正确的16进制值 或 HASH值\n'
        input = self.bin(self.textEditSM2SignMessage)
        
        if input is not None:        
            if self.size(self.lineEditID) == 0:
                error += '请输入正确的ID\n'          
        if len(error) != 0:
            self.showError(error)
            return

        pk = self.bin(self.lineEditSM2PKSign)
        ida = self.bin(self.lineEditID)
        sign = self.bin(self.textEditSM2SignOutput)
        digest = self.bin(self.lineEditSM2SignHash)        
        
        sm2 = SM2(pk)
        if len(input) != 0:
            result = sm2.verify(
                sign, input, ida=ida,
                zHook=lambda z: self.setBin(self.lineEditZ, z),
                digestHook=lambda digest: self.setBin(self.lineEditSM2SignHash, digest))
        else:
            result = sm2.verifyDigest(sign, digest)
        if result:
            QMessageBox.information(self, '正确', '验签通过')
        else:
            QMessageBox.warning(self, '错误', '验签不通过')

    def sm2Sign(self):
        error = ''
        if self.size(self.lineEditSM2PKSign) != SM2.PublicKeySize:
            error += f'请输入正确的16进制公钥值，公钥长应为 {SM2.PublicKeySize}\n'
        if self.size(self.lineEditSM2SKSign) != SM2.PrivateKeySize:
            error += f'请输入正确的16进制私钥值，公钥长应为 {SM2.PrivateKeySize}\n'
        if self.size(self.textEditSM2SignMessage) == 0 and self.size(self.lineEditSM2SignHash) == 0:
            error += '请输入正确的16进制值 或 HASH值\n'                   
        if self.isFixSm2SignRandom and self.size(self.lineEditSM2RandomSign) != SM2.RandomSize:
            error += f'请输入正确的随机数，随机数长应为 {SM2.RandomSize}'  

        input = self.bin(self.textEditSM2SignMessage)
        
        if len(input) != 0:
            if self.size(self.lineEditID) == 0:
                error += '请输入正确的ID\n'          
        if len(error) != 0:
            self.showError(error)
            return

        pk = self.bin(self.lineEditSM2PKSign)
        sk = self.bin(self.lineEditSM2SKSign)
        ida = self.bin(self.lineEditID)    
        digest = self.bin(self.lineEditSM2SignHash)
        
        random = None
        if self.isFixSm2SignRandom:            
            random = self.bin(self.lineEditSM2RandomSign)
        sm2 = SM2(pk, sk)
        if input is not None:
            output = sm2.sign(
                input, ida=ida, random=random, 
                zHook=lambda z: self.setBin(self.lineEditZ, z),
                randomHook=lambda random: self.setBin(self.lineEditSM2RandomSign, random),
                digestHook=lambda digest: self.setBin(self.lineEditSM2SignHash, digest)
            )
        else:
            output = sm2.signDigest(digest, random)
        self.setBin(self.textEditSM2SignOutput, output)

    def getENTL(self, ida):
        try:
            ida = bytes.fromhex(ida)
            size = len(ida) * 8
        except:
            size = 0
        size = size.to_bytes(2, 'big')
        self.setBin(self.lineEditENTL, size)

    def sm2Reset(self):
        self.setBin(self.lineEditENTL, SM2.ENTL)
        self.setBin(self.lineEditID, SM2.ID)
        self.setBin(self.lineEditA, SM2.A)
        self.setBin(self.lineEditB, SM2.B)
        self.setBin(self.lineEditxG, SM2.XG)
        self.setBin(self.lineEdityG, SM2.YG)

    def sm2GetZ(self):
        error = ''
        if self.size(self.lineEditSM2PKSign) != SM2.PublicKeySize:
            error += '请输入正确的16进制公钥值\n'
        if self.size(self.lineEditID) == 0:
            error += '请输入正确的ID\n'
        if len(error) != 0:
            self.showError(error)
            return        
        pk = self.bin(self.lineEditSM2PKSign)
        ida = self.bin(self.lineEditID)
        z = SM2(pk).getZ(ida)
        self.setBin(self.lineEditZ, z)

    def randomHashInput(self):
        input = randomBytes(random.randint(1, 4096))
        self.setBin(self.textEditHashInput, input)        

    def checkHash(self):
        error = ''
        if self.size(self.textEditHashInput) == 0:
            error += "请输入正确的数据"
            return False, error

        if (len(error) == 0):
            return True, None
        return False, error
        
    def hash(self, how):
        ok, error = self.checkHash()
        if not ok:
            self.showError(error)
            return
        h = how(self.bin(self.textEditHashInput))
        v = h.digest()
        self.setBin(self.lineEditHashOutput, v)

    def SM3(self):
        self.hash(SM3)

    def MD5(self):
        self.hash(MD5)

    def SHA1(self):
        self.hash(SHA1)

    def SHA256(self):
        self.hash(SHA256)

    def randomSm2SignMessage(self):
        pass

    def randomChecksumInput(self):
        pass

    def CRC16(self):
        pass

    def CRC32(self):
        pass

    def XOR(self):
        pass

    def checkChar(self):
        pass

    def exchangeCharInput(self):
        input = self.text(self.textEditCharInput)
        output = self.text(self.textEditCharOutput)
        self.setText(self.textEditCharInput, output, upper=False)
        self.setText(self.textEditCharOutput, input, upper=False)

    def setEncoding(self, checked):
        if not checked:
            return
        if self.sender() == self.radioButtonEncodingGBK:
            self.encoding = self.ENCODING_GBK
        elif self.sender() == self.radioButtonEncodingUTF8:
            self.encoding = self.ENCODING_UTF8

    def randomCharInput(self):
        pass

    def readBin(self):
        fileName, _ = QFileDialog.getOpenFileName(self,  
                                    "文件保存",  
                                    None, # 起始路径 
                                    "Bin Files (*.bin);;All Files (*)")  
        if fileName == "":
            return
        with open(fileName, 'rb') as f:
            b = f.read()
        self.setBin(self.textEditCharInput, b)

    def saveAsBin(self):
        if self.size(self.textEditCharInput) == 0:
            error = "请输入正确的16进制数据"
            self.showError(error)
            return   
        b = self.bin(self.textEditCharInput)

        fileName, _ = QFileDialog.getSaveFileName(self,  
                                    "文件保存",  
                                    None, # 起始路径 
                                    "Bin Files (*.bin);;All Files (*)")  

        if fileName == "":
            return
        with open(fileName, 'wb') as f:
            f.write(b)

    def base64Encode(self):
        if self.size(self.textEditCharInput) == 0:
            error = "请输入正确的16进制数据"
            self.showError(error)
            return
        b = base64.b64encode(self.bin(self.textEditCharInput))
        self.setText(self.textEditCharOutput, str(b, encoding='utf-8'), upper=False)

    def base64Decode(self):
        if len(self.text(self.textEditCharInput)) == 0:
            error = "请输入base64编码数据"
            self.showError(error)
            return
        try:
            b = base64.b64decode(self.text(self.textEditCharInput))
            self.setBin(self.textEditCharOutput, b)
        except:
            error = "请输入正确的base64编码数据"
            self.showError(error)
            return            

    def ascii2HexString(self):
        if len(self.text(self.textEditCharInput)) == 0:
            error = "请输入字符"
            self.showError(error)
            return
        s = self.text(self.textEditCharInput)
        try:
            b = bytes(s, encoding=self.encoding)
        except:
            error = f"请输入正确的{self.encoding}数据"
            self.showError(error)
            return            
        self.setBin(self.textEditCharOutput, b)            
        

    def hexString2Ascii(self):
        if self.size(self.textEditCharInput) == 0:
            error = "请输入正确的16进制数据"
            self.showError(error)
            return
        b = self.bin(self.textEditCharInput)
        try:
            s = str(b, encoding=self.encoding)
        except:
            error = f"请输入正确的{self.encoding}数据"
            self.showError(error)
            return            
        self.setText(self.textEditCharOutput, s, upper=False)

    def toCArray(self):
        if self.size(self.textEditCharInput) == 0:
            error = "请输入正确的16进制数据"
            self.showError(error)
            return
        b = self.bin(self.textEditCharInput)
        c = ""        
        for i in range(0, len(b)):
            c += f'0x{b[i:i+1].hex().upper()}, '
            if (i + 1) % 10 == 0:
                c += '\n'
        if c.endswith('\n'):
            c = c[:-1]
        if c.endswith(', '):
            c = c[:-2]
        self.setText(self.textEditCharOutput, c, upper=False)

  
    def setRsaSignHash(self, checked):
        if not checked:
            return
        if self.sender() == self.radioButtonWithMD5:
            self.rsaSignHash =MD5
        elif self.sender() == self.radioButtonWithSHA1:
            self.rsaSignHash = SHA1
        elif self.sender() == self.radioButtonWithSHA256:
            self.rsaSignHash = SHA256

    def fixSm2EncryptRandom(self, checked):
        self.isFixSm2EncryptRandom = checked

    def fixSm2SignRandom(self, checked):         
        self.isFixSm2SignRandom = checked

    def setSymmetricAutoPad(self, checked):
        self.isSymmetricAutoPad = checked 

    def about(self):
        about = """加解密工具箱
LXL
V1.0  完成既定设计功能"""
        QMessageBox.information(self, 'About', about)